import xml.etree.ElementTree as ET

import gevent
import jenkins
from gevent import monkey

monkey.patch_all()


class JenkinsAPIException(Exception):
    """Base class for all errors
    """
    pass


class Jenkins_job(object):

    def __init__(self, url, user, passwd):
        self.server = jenkins.Jenkins(url, username=user, password=passwd)

    def get_jenkins_version(self):
        return self.server.get_version()

    def get_job_config(self, job_name):
        """
        job_name: path:str
        """
        return self.server.get_job_config(job_name)

    def create_job(self, job_name, job_config):
        """
        job_name: path:str
        job_config: xml:str
        """
        try:
            res = self.server.create_job(job_name, job_config)
            print("create job %s succeed." % job_name)
            return True
        except JenkinsAPIException as e:
            print(e)
            print("create job %s failed." % job_name)
            return False

    @staticmethod
    def get_job_pkg_dict(packages, release_jobs):
        """
        """
        num_packages = len(packages)
        num_jobs = len(release_jobs)

        count = num_packages // num_jobs
        index = 0
        job_pkg_dict = dict()
        for job in release_jobs:
            job_pkg_dict[job] = packages[index * count: (index + 1) * count]
            index += 1

        rest_idx = 0
        if packages[index * count:]:
            for pkg in packages[index * count:]:
                job_pkg_dict[release_jobs[rest_idx]].append(pkg)
                rest_idx += 1
        return job_pkg_dict

    def update_config(self, target_job, template_job_config):
        # root = ET.fromstring(self._template_job_config.encode("utf-8"))
        # return ET.tostring(root).decode('utf-8')
        print("=========== self.template_job_config", template_job_config)
        # root = ET.parse(self._template_job_config.encode("utf-8"))
        root = ET.fromstring(template_job_config.encode("utf-8"))

        # 修改trigger
        ele_projects = root.find("publishers/hudson.plugins.parameterizedtrigger.BuildTrigger//projects")
        print("======== ele_projects ==========", ele_projects)
        if ele_projects is not None:
            print("ele_projects.text: ", ele_projects.text)
            ele_projects.text = ",".join(trigger_jobs)
            print("ele_projects.text: ", ele_projects.text)
            print("update trigger successfully")

        # 修改任务模板
        ele_cmd = root.find("builders/hudson.tasks.Shell//command")
        # job_pkg_dict = self.get_job_pkg_dict(packages, list(packages_dct.values()))
        # print(job_pkg_dict)
        # verify_pkgs = ",".join(job_pkg_dict[target_job])
        if ele_cmd is not None:
            verify_pkgs = ",".join(packages_dct.get(target_job))
            ele_cmd.text = ele_cmd.text.strip() + " " + verify_pkgs
            print("update job successfully")

        # 修改comment
        ele_comment = root.find("publishers/join.JoinTrigger//projects")
        if ele_comment is not None:
            ele_comment.text = comment_job
            print("update comment successfully")
        return ET.tostring(root).decode('utf-8')


    def create_folder(self, folder):
        self.server.create_folder(folder, True)

    def run(self, template_job, jobs, concurrency=75, retry=3, interval=0):
        """
        启动
        :param jobs: 任务列表
        :param concurrency: 并发量
        :param retry: 尝试次数
        :param interval: 每次batch请求后sleep时间（秒），
        :return:
        """
        # temp_job_config = self.get_job_config(template_job)
        print("jobs {}".format(jobs))

        # updated_config = self.update_config(jobs, packages, temp_job_config)

        def run_once(target_jobs):
            """
            run once for retry
            """
            batch = int((len(target_jobs) + concurrency - 1) / concurrency)
            _failed_jobs = []
            for index in range(batch):
                works = [gevent.spawn(self.dispatch, job, template_job)
                         for job in target_jobs[index * concurrency: (index + 1) * concurrency]]
                # logger.info("{} works, {}/{} ".format(len(works), index + 1, batch))
                print("{} works, {}/{} ".format(len(works), index + 1, batch))
                gevent.joinall(works)
                print("========== works:%s =========" % works)
                for work in works:
                    if work.value["result"]:
                        # logger.info("{} job {} ... ok".format(action, work.value["job"]))
                        print("create job {} ... ok".format(work.value["job"]))
                    else:
                        _failed_jobs.append(work.value["job"])
                        # logger.error("{} job {} ... failed".format(action, work.value["job"]))
                        print("create job {} ... failed".format(work.value["job"]))
            return _failed_jobs

        failed_jobs = run_once(jobs)

        for index in range(retry):
            if not failed_jobs:
                break
            # logger.info("{} jobs failed, retrying {}/{}".format(len(failed_jobs), index + 1, retry))
            print("{} jobs failed, retrying {}/{}".format(len(failed_jobs), index + 1, retry))
            failed_jobs = run_once(failed_jobs)

        if failed_jobs:
            print("{} failed jobs".format(len(failed_jobs)))
            # logger.warning("{} failed jobs".format(len(failed_jobs)))
            # logger.warning("{}{}".format(",".join(failed_jobs[:100]), "..." if len(failed_jobs) > 100 else ""))

    def dispatch(self, job, template_job):
        """
        分发任务
        :param template_job: template job
        :param job: 目标任务
        :return:
        """
        temp_job_config = self.get_job_config(template_job)
        updated_config = self.update_config(job, temp_job_config)
        result = self.create_job(job, updated_config)
        return {"job": job, "result": result}


if __name__ == '__main__':
    server = Jenkins_job('https://jenkins.openeuler.org', 'tuShenmei', '062725tu_TU')
    server.create_folder('function-item/release-manager/openeuler-202106281604')
    server.create_folder('function-item/release-manager/openeuler-202106281604/aarch64')
    server.create_folder('function-item/release-manager/openeuler-202106281604/x86-64')
    template_job = 'function-item/release-manager/20210622172828/aarch64/2-2'
    # template_job = 'function-item/release-manager/20210622172828/trigger'
    packages = ["gcc", "glibc", "git"]
    jobs = [
        # 'function-item/release-manager/20210622172828/aarch64/2-6',
        # 'function-item/release-manager/20210622172828/aarch64/2-7',
        # 'function-item/release-manager/20210622172828/aarch64/2-8',
        # 'function-item/release-manager/20210622172828/aarch64/2-11',
        # 'function-item/release-manager/20210622172828/aarch64/2-12',
        # 'function-item/release-manager/20210622172828/aarch64/2-13',
        # 'function-item/release-manager/20210622172828/aarch64/2-14',
        'function-item/release-manager/openeuler-202106281604/aarch64/2-15',
        # 'function-item/release-manager/20210622172828/aarch64/2-8',
        # 'function-item/release-manager/20210622172828/trigger_3'
    ]
    packages_dct = {
        # 'function-item/release-manager/20210622172828/aarch64/2-11': ["gcc", "glibc", "git"],
        # 'function-item/release-manager/20210622172828/aarch64/2-12': ["aaa", "bb", "cc"],
        # 'function-item/release-manager/20210622172828/aarch64/2-13': ["python", "perl"],
        # 'function-item/release-manager/20210622172828/aarch64/2-14': ["python", "perl"],
        'function-item/release-manager/openeuler-202106281604/aarch64/2-15': ["python", "perl"],
        # "function-item/release-manager/20210622172828/trigger_3": []
    }

    trigger_jobs = [
        'function-item/release-manager/20210622172828/aarch64/2-6',
        'function-item/release-manager/20210622172828/aarch64/2-7',
        'function-item/release-manager/20210622172828/aarch64/2-8',
    ]
    comment_job = "function-item/release-manager/20210622172828/comment_1"

    server.run(template_job, jobs)




